package ua.sumdu.LukyanovChalyi.Server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.SAXException;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;

import ua.sumdu.LukyanovChalyi.Utils.*;

public class ReadWriteXMLFile implements ReadWriteFiles {

    private XMLUtils utils;

    public ReadWriteXMLFile() {
        utils = new XMLUtils();
    }

    public void write(OutputStream os, Map<Long, CopyBook> books) throws IOException {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.newDocument();
            Element root = doc.createElement(XMLUtils.LIBRARY);
            Set<Long> keys = books.keySet();
            for (Long key : keys) {
                root.appendChild(utils.makeCopyBook(doc, books.get(key)));
            }
            doc.appendChild(root);
            StreamResult result = new StreamResult(os);
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();
            transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "test.dtd");
            transformer.transform(new DOMSource(doc), result);
        } catch (TransformerException e) {
            throw new IOException("TransformerException while parsing", e);
        } catch (ParserConfigurationException e) {
            throw new IOException("ParserConfigurationException while parsing", e);
        }
    }

    public Map<Long, CopyBook> read(InputStream is) throws IOException {
        Map<Long, CopyBook> map = null;
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setValidating(true);
            DocumentBuilder builder = factory.newDocumentBuilder();

            Document doc = builder.parse(is);
            map = new HashMap<Long, CopyBook>();
            Element library = doc.getDocumentElement();
            NodeList books = library.getElementsByTagName(XMLUtils.COPY_BOOK);

            for (int i = 0; i < books.getLength(); i++) {
                CopyBook addBook = utils.readCopyBook((Element) books.item(i));
                map.put(addBook.getID(), addBook);
            }
        } catch (ParserConfigurationException e) {
            throw new ModelException("ParserConfigurationException while parsing. Can't read books from file", e);
        } catch (SAXException e) {
            throw new ModelException("SAXException while parsing. Can't read books from file", e);
        } catch (NumberFormatException e) {
            throw new ModelException("Parse invalid number(s) from file. Can't read books..", e);
        }
        return map;
    }

    @Override
    public Map<Long, CopyBook> read(String filename) throws IOException {
        return read(new BufferedInputStream(new FileInputStream(filename)));
    }

    @Override
    public void write(Map<Long, CopyBook> books, String filename) throws IOException {
        write(new BufferedOutputStream(new FileOutputStream(filename)), books);
    }
}